package xyz.erupt.flow.core.engine;

import com.alibaba.fastjson.JSON;
import org.camunda.bpm.engine.RepositoryService;
import org.camunda.bpm.engine.delegate.TaskListener;
import org.camunda.bpm.model.bpmn.Bpmn;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
import org.camunda.bpm.model.bpmn.builder.ProcessBuilder;
import org.camunda.bpm.model.bpmn.instance.*;
import org.camunda.bpm.model.bpmn.instance.Process;
import org.camunda.bpm.model.bpmn.instance.camunda.CamundaExecutionListener;
import org.camunda.bpm.model.bpmn.instance.camunda.CamundaField;
import org.camunda.bpm.model.bpmn.instance.camunda.CamundaString;
import org.camunda.bpm.model.bpmn.instance.camunda.CamundaTaskListener;
import org.camunda.bpm.model.xml.ModelInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import xyz.erupt.core.exception.EruptApiErrorTip;
import xyz.erupt.flow.bean.entity.OaWorkFlowDefinition;
import xyz.erupt.flow.bean.entity.node.OaProcessNode;
import xyz.erupt.flow.bean.entity.node.OaProcessNodeProps;
import xyz.erupt.flow.constant.FlowConstant;
import xyz.erupt.flow.core.bpmn.BpmnAutoLayout;
import xyz.erupt.flow.core.engine.formItems.WorkFlowFormField;
import xyz.erupt.flow.core.enums.WorkFlowTaskType;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @description: 工作流构建
 * @className: WorkFlowBuilder
 * @author: PP
 * @date: 2024/10/19 9:54
 * @Version: 1.0
 */
@Service
public class WorkFlowBuilder {
    @Autowired
    private RepositoryService repositoryService;

    /**
     * 构建camunda工作流并部署
     * @param workFlowDefinition
     * @return
     */
    public void build(OaWorkFlowDefinition workFlowDefinition){
        if (workFlowDefinition == null) {
            throw new EruptApiErrorTip("不存在该工作流!");
        }
        String xml = CreateCamundaText(workFlowDefinition);
        //部署
        repositoryService.createDeployment()
                .name(workFlowDefinition.getFormName())
                .addString(workFlowDefinition.getFormName()+".bpmn", xml)
                .deploy();
    }

    /**
     * 创建camunda工作流字符串
     * @param workFlowDefinition
     */
    public String CreateCamundaText(OaWorkFlowDefinition workFlowDefinition) {
        //获取流程节点数据
        OaProcessNode processNode = workFlowDefinition.getProcessNode();

        //创建camunda工作流
        ProcessBuilder processBuilder = Bpmn.createExecutableProcess("_"+workFlowDefinition.getId().toString());//使用_processId设置工作流键,id必须是字符或下划线开头
        Process process = processBuilder.getElement();
        ModelInstance modelInstance = process.getModelInstance();

        //创建开始事件
        StartEvent startEvent = modelInstance.newInstance(StartEvent.class);
        startEvent.setId("start");
        startEvent.setName("开始");
        process.addChildElement(startEvent);

        //设置发起人任务
        UserTask startUserTask = modelInstance.newInstance(UserTask.class);
        startUserTask.setId(processNode.getId());//使用根节点root作为发起人任务键
        startUserTask.setName(processNode.getName());//发起人
        insertStartUser(startUserTask); //设置发起人用户
        /************************准备弃用start******************/
//        workFlowFormField.AddFormFields(modelInstance,startUserTask,workFlowDefinition);
        /************************准备弃用end******************/
        process.addChildElement(startUserTask);

        //开始事件-发起人连线
        SequenceFlow sequenceFlow = modelInstance.newInstance(SequenceFlow.class);
        sequenceFlow.setId(startEvent.getId()+"-"+startUserTask.getId());
        process.addChildElement(sequenceFlow);
        sequenceFlow.setSource(startEvent);
        startEvent.getOutgoing().add(sequenceFlow);
        sequenceFlow.setTarget(startUserTask);
        startUserTask.getIncoming().add(sequenceFlow);

        ArrayList<FlowNode> end_Nodes = new ArrayList<>();
        //设置子节点（存在四种情况，1.正常任务，2.条件分支，3.并行分支，4.只有发起人情况）
        if (processNode.getChildren() != null && processNode.getChildren().getId()!=null && !processNode.getChildren().getId().isEmpty()){//判断是否存在子节点（包含1,2,3种情况）
            createCamundaChildrenText(processNode.getChildren(),startUserTask,modelInstance,workFlowDefinition,process,end_Nodes,null);
        }

        if (!end_Nodes.isEmpty()){//将所有最终节点连到结束事件
            //创建结束事件
            EndEvent endEvent = modelInstance.newInstance(EndEvent.class);
            endEvent.setId("end");
            endEvent.setName("结束");
            process.addChildElement(endEvent);

            for (FlowNode s : end_Nodes) {
                //创建连线
                SequenceFlow sequenceFlow1 = modelInstance.newInstance(SequenceFlow.class);
                sequenceFlow1.setId(s.getId()+"-"+endEvent.getId());
                process.addChildElement(sequenceFlow1);
                sequenceFlow1.setSource(s);
                s.getOutgoing().add(sequenceFlow1);
                sequenceFlow1.setTarget(endEvent);
                endEvent.getIncoming().add(sequenceFlow1);
            }
        }else {//第4种情况(只有发起人情况)
            //创建结束事件
            EndEvent endEvent = modelInstance.newInstance(EndEvent.class);
            endEvent.setId("end");
            endEvent.setName("结束");
            process.addChildElement(endEvent);

            //创建连线
            SequenceFlow sequenceFlow1 = modelInstance.newInstance(SequenceFlow.class);
            sequenceFlow1.setId(startUserTask.getId()+"-"+endEvent.getId());
            process.addChildElement(sequenceFlow1);
            sequenceFlow1.setSource(startUserTask);
            startUserTask.getOutgoing().add(sequenceFlow1);
            sequenceFlow1.setTarget(endEvent);
            endEvent.getIncoming().add(sequenceFlow1);
        }

        //自动布局
        BpmnModelInstance bpmnModelInstance = processBuilder.done();
        new BpmnAutoLayout(bpmnModelInstance).execute();//自动布局，camunda流程编辑器可查看
        String xml = Bpmn.convertToString(bpmnModelInstance);//转为字符串
        return xml;
    }

    private void insertStartUser(UserTask startUserTask) {
        startUserTask.setCamundaAssignee("${startUser}");
    }

    /**
     * 创建子节点camunda流程数据
     * @param children
     * @param modelInstance
     * @param process
     * @param branchId 不是分支时候，输入null，有分支数据时候传入
     */
    private void createCamundaChildrenText(OaProcessNode children, FlowNode parent, ModelInstance modelInstance, OaWorkFlowDefinition workFlowDefinition,Process process,List<FlowNode> end_nodes,String branchId) {
        if(checkUserTask(children)){//普通任务
            //创建用户普通任务(审批人，抄送人)
            UserTask userTask = modelInstance.newInstance(UserTask.class);
            userTask.setId(children.getId());
            userTask.setName(children.getName());
//            workFlowFormField.AddFormFields(modelInstance,userTask,workFlowDefinition);

            process.addChildElement(userTask);

            //创建连线
            SequenceFlow lastFlow = insetInclusiveGatewayDelegate(modelInstance, process, parent, userTask, branchId);

            //插入用户操作并分配
            insertUserTaskUsers(modelInstance,children,lastFlow,userTask);

            //生成子节点
            if(children.getChildren()!=null&&children.getChildren().getId()!=null && !children.getChildren().getId().isEmpty()){
                createCamundaChildrenText(children.getChildren(),userTask,modelInstance,workFlowDefinition,process,end_nodes,null);
            }else {//无子节点则作为最后节点
                end_nodes.add(userTask);
            }

        }else if(checkCONCURRENTS(children)){//并行分支
            //创建并行网关
            ParallelGateway parallelGateway = modelInstance.newInstance(ParallelGateway.class);
            parallelGateway.setId(children.getId());
            parallelGateway.setName(children.getName());
            process.addChildElement(parallelGateway);

            //创建连线
            insetInclusiveGatewayDelegate(modelInstance,process,parent,parallelGateway,branchId);

            //创建分支最后任务的列表
            ArrayList<FlowNode> endNodes = new ArrayList<>();
            //创建并行分支（不需要顺序）
            if(!children.getBranchs().isEmpty()){
                for (OaProcessNode branch : children.getBranchs()) {
                    if (branch.getChildren()!=null&&branch.getChildren().getId()!=null &&!branch.getChildren().getId().isEmpty()){//CONCURRENT不建立任务节点
                        createCamundaChildrenText(branch.getChildren(),parallelGateway,modelInstance,workFlowDefinition,process,endNodes,null);//递归调用创建分支节点
                    }
                }
            }

            //创建并行分支，后续children节点
            if (children.getChildren()!=null&&children.getChildren().getId()!=null&&!children.getChildren().getId().isEmpty()&&children.getChildren().getType().equals(WorkFlowTaskType.NODE_EMPTY)){
                //闭合并行网关
                OaProcessNode endNode = children.getChildren();
                ParallelGateway parallelGateway_end = modelInstance.newInstance(ParallelGateway.class);
                parallelGateway_end.setId(endNode.getId());
                process.addChildElement(parallelGateway_end);

                //创建各个结束节点同结束的网关连线
                if (!endNodes.isEmpty()){
                    for (FlowNode s : endNodes) {
                        SequenceFlow end_sequenceFlow = modelInstance.newInstance(SequenceFlow.class);
                        end_sequenceFlow.setId(s.getId()+"-"+parallelGateway_end.getId());
                        process.addChildElement(end_sequenceFlow);
                        end_sequenceFlow.setSource(s);
                        s.getOutgoing().add(end_sequenceFlow);
                        end_sequenceFlow.setTarget(parallelGateway_end);
                        parallelGateway_end.getIncoming().add(end_sequenceFlow);
                    }
                }else {//无最后节点
//                    网关和网关连线
                    SequenceFlow end_sequenceFlow = modelInstance.newInstance(SequenceFlow.class);
                    end_sequenceFlow.setId(parallelGateway.getId()+"-"+parallelGateway_end.getId());
                    process.addChildElement(end_sequenceFlow);
                    end_sequenceFlow.setSource(parallelGateway);
                    parallelGateway.getOutgoing().add(end_sequenceFlow);
                    end_sequenceFlow.setTarget(parallelGateway_end);
                    parallelGateway_end.getIncoming().add(end_sequenceFlow);
                }

                //该节点是在EMPTY下的children中
                if (children.getChildren().getChildren()!=null && children.getChildren().getChildren().getId()!=null && !children.getChildren().getChildren().getId().isEmpty()){
                    createCamundaChildrenText(children.getChildren().getChildren(),parallelGateway_end,modelInstance,workFlowDefinition,process,end_nodes,null);
                }else {//反之则说明该节点为最终节点
                    end_nodes.add(parallelGateway_end);
                }

            }


        } else if (checkCONDITIONS(children)) {//条件分支
            //创建包含网关，意味着只要条件满足就能继续执行
            InclusiveGateway inclusiveGateway = modelInstance.newInstance(InclusiveGateway.class);
            inclusiveGateway.setId(children.getId());
            inclusiveGateway.setName(children.getName());
            process.addChildElement(inclusiveGateway);

            //创建连线
            insetInclusiveGatewayDelegate(modelInstance,process,parent,inclusiveGateway,branchId);
            //创建分支最后任务的列表
            ArrayList<FlowNode> endNodes = new ArrayList<>();
            //创建包含网关分支（不需要顺序)
            if(!children.getBranchs().isEmpty()){
                for (OaProcessNode branch : children.getBranchs()) {
                    if (branch.getChildren()!=null&&branch.getChildren().getId()!=null &&!branch.getChildren().getId().isEmpty()){//CONDITION不建立任务节点
                        createCamundaChildrenText(branch.getChildren(),inclusiveGateway,modelInstance,workFlowDefinition,process,endNodes, branch.getId());//递归调用创建分支节点
                    }
                }
            }

            //创建条件分支，后续children节点
            if (children.getChildren()!=null&&children.getChildren().getId()!=null&&!children.getChildren().getId().isEmpty()&&children.getChildren().getType().equals(WorkFlowTaskType.NODE_EMPTY)){
                //闭合包含网关
                OaProcessNode endNode = children.getChildren();
                InclusiveGateway inclusiveGateway_end = modelInstance.newInstance(InclusiveGateway.class);
                inclusiveGateway_end.setId(endNode.getId());
                process.addChildElement(inclusiveGateway_end);

                //创建各个结束节点同结束的网关连线
                if (!endNodes.isEmpty()){
                    for (FlowNode s : endNodes) {
                        SequenceFlow end_sequenceFlow = modelInstance.newInstance(SequenceFlow.class);
                        end_sequenceFlow.setId(s.getId()+"-"+inclusiveGateway_end.getId());
                        process.addChildElement(end_sequenceFlow);
                        end_sequenceFlow.setSource(s);
                        s.getOutgoing().add(end_sequenceFlow);
                        end_sequenceFlow.setTarget(inclusiveGateway_end);
                        inclusiveGateway_end.getIncoming().add(end_sequenceFlow);
                    }
                }else {//无最后节点
//                    网关和网关连线
                    SequenceFlow end_sequenceFlow = modelInstance.newInstance(SequenceFlow.class);
                    end_sequenceFlow.setId(inclusiveGateway.getId()+"-"+inclusiveGateway_end.getId());
                    process.addChildElement(end_sequenceFlow);
                    end_sequenceFlow.setSource(inclusiveGateway);
                    inclusiveGateway.getOutgoing().add(end_sequenceFlow);
                    end_sequenceFlow.setTarget(inclusiveGateway_end);
                    inclusiveGateway_end.getIncoming().add(end_sequenceFlow);
                }
                //该节点是在EMPTY下的children中
                if (children.getChildren().getChildren()!=null && children.getChildren().getChildren().getId()!=null && !children.getChildren().getChildren().getId().isEmpty()){
                    createCamundaChildrenText(children.getChildren().getChildren(),inclusiveGateway_end,modelInstance,workFlowDefinition,process,end_nodes,null);
                }else {//反之则说明该节点为最终节点
                    end_nodes.add(inclusiveGateway_end);
                }

            }
        }


    }

    /**
     * 插入用户任务用户分配
     * @param modelInstance
     * @param userTask
     * @param currentNode 当前节点
     */
    private void insertUserTaskUsers(ModelInstance modelInstance,OaProcessNode currentNode,SequenceFlow sequenceFlow, UserTask userTask) {
        //创建并行或串行任务
        MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = modelInstance.newInstance(MultiInstanceLoopCharacteristics.class);
        multiInstanceLoopCharacteristics.setCamundaCollection("userList");
        multiInstanceLoopCharacteristics.setCamundaElementVariable("user");

        if(checkNext(currentNode)){//顺序会签
            multiInstanceLoopCharacteristics.setSequential(true);
        }
        CompletionCondition completionCondition = modelInstance.newInstance(CompletionCondition.class);

        if(checkOr(currentNode)){//判断是否为或签
            completionCondition.setTextContent("${nrOfCompletedInstances==1}");
        }else {
            completionCondition.setTextContent("${nrOfInstances==nrOfCompletedInstances}");
        }
        multiInstanceLoopCharacteristics.setCompletionCondition(completionCondition);
        userTask.addChildElement(multiInstanceLoopCharacteristics);
        userTask.setCamundaAssignee("${user}");//设置用户

        //创建监听任务并注入字段(在连线take)
        ExtensionElements extensionElements = modelInstance.newInstance(ExtensionElements.class);
        CamundaExecutionListener camundaExecutionListener = modelInstance.newInstance(CamundaExecutionListener.class);
        camundaExecutionListener.setCamundaDelegateExpression("${UserTaskDelegate}");
        camundaExecutionListener.setCamundaEvent("take");//创建该任务时候执行分配
        CamundaField camundaField = modelInstance.newInstance(CamundaField.class);
        camundaField.setCamundaName("props");//设置变量参数名
        CamundaString camundaString = modelInstance.newInstance(CamundaString.class);
        camundaString.setTextContent(JSON.toJSONString(currentNode.getProps()));//传入props字符串变量
        camundaField.setCamundaString(camundaString);
        camundaExecutionListener.addChildElement(camundaField);
        extensionElements.addChildElement(camundaExecutionListener);
        sequenceFlow.setExtensionElements(extensionElements);

    }

    /**
     * 判断是否为或签
     * @param currentNode
     * @return
     */
    private boolean checkOr(OaProcessNode currentNode) {
       if (currentNode!=null&&!currentNode.getId().isEmpty()){
           OaProcessNodeProps props = currentNode.getProps();
           if (props!=null){
               if(props.getMode().equals(FlowConstant.COMPLETE_MODE_OR)){
                   return true;
               }else {
                   return false;
               }
           }else {
               return false;
           }
       }else {
           return false;
       }
    }

    /**
     * 判断是否顺序会签
     * @param currentNode
     * @return
     */
    private boolean checkNext(OaProcessNode currentNode) {
        if(currentNode!=null&&!currentNode.getId().isEmpty()){
            if (currentNode.getProps()!=null){
                OaProcessNodeProps props = currentNode.getProps();
                if (props.getMode().equals(FlowConstant.COMPLETE_MODE_NEXT)){
                    return true;
                }else {
                    return false;
                }
            }else {
                return false;
            }
        }else {//无数据选择并行执行
            return false;
        }
    }

    /**
     * 判别是否为条件分支隐藏节点
     * @param children
     * @return
     */
    private boolean checkCONDITIONS(OaProcessNode children) {
        return children.getType().equals(WorkFlowTaskType.NODE_CONDITIONS);
    }

    /**
     * 判别是否为并行隐藏节点
     * @param children
     * @return
     */
    private boolean checkCONCURRENTS(OaProcessNode children) {
        return children.getType().equals(WorkFlowTaskType.NODE_CONCURRENTS);
    }

    /**
     * 判别是否为普通任务（不含发起人任务，不含分支节点及隐藏节点）
     * @param children
     * @return
     */
    private boolean checkUserTask(OaProcessNode children) {
        return  !children.getType().equals(WorkFlowTaskType.NODE_ROOT)&&
                !children.getType().equals(WorkFlowTaskType.NODE_CONCURRENTS)&&
                !children.getType().equals(WorkFlowTaskType.NODE_CONCURRENT)&&
                !children.getType().equals(WorkFlowTaskType.NODE_CONDITIONS)&&
                !children.getType().equals(WorkFlowTaskType.NODE_CONDITION);
    }


    /**
     * 将xml储存为camunda文件
     * @param xml
     * @param fileName
     */
    public static void saveXmlToFile(String xml, String fileName) {
        try (FileWriter fileWriter = new FileWriter(new File(fileName))) {
            fileWriter.write(xml);
            System.out.println("XML 文件生成成功: " + fileName);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置包含网关条件表达数据(*************************前加一个系统任务来存储id)返回该节点操作后的back任务前的连线
     */
    private SequenceFlow insetInclusiveGatewayDelegate(ModelInstance modelInstance,Process process,FlowNode front,FlowNode back,String propsId){
        SequenceFlow lastFlow;
        if (propsId!=null&&!propsId.isEmpty()){//不是空输入则属于包含网关
            ServiceTask serviceTask = modelInstance.newInstance(ServiceTask.class);//创建系统任务
            serviceTask.setId(propsId);
            process.addChildElement(serviceTask);

            //添加系统任务实现
            serviceTask.setCamundaDelegateExpression("${InclusiveGatewayDelegate}");

            //创建连线
            SequenceFlow sequenceFlow = modelInstance.newInstance(SequenceFlow.class);//第一个，默认通过
            sequenceFlow.setId(front.getId()+"-"+serviceTask.getId());
            process.addChildElement(sequenceFlow);

            sequenceFlow.setSource(front);
            front.getOutgoing().add(sequenceFlow);
            sequenceFlow.setTarget(serviceTask);
            serviceTask.getIncoming().add(sequenceFlow);

            SequenceFlow sequenceFlow1 = modelInstance.newInstance(SequenceFlow.class);
            sequenceFlow1.setId(serviceTask.getId()+"-"+back.getId());
            process.addChildElement(sequenceFlow1);

            sequenceFlow1.setSource(serviceTask);
            serviceTask.getOutgoing().add(sequenceFlow1);
            sequenceFlow1.setTarget(back);
            back.getIncoming().add(sequenceFlow1);

            //添加表达式
            ConditionExpression conditionExpression = modelInstance.newInstance(ConditionExpression.class);
            conditionExpression.setTextContent("${status}");
            sequenceFlow1.setConditionExpression(conditionExpression);

            lastFlow = sequenceFlow1;

        }else {//正常处理
            SequenceFlow sequenceFlow = modelInstance.newInstance(SequenceFlow.class);
            sequenceFlow.setId(front.getId()+"-"+back.getId());
            process.addChildElement(sequenceFlow);
            sequenceFlow.setSource(front);
            front.getOutgoing().add(sequenceFlow);
            sequenceFlow.setTarget(back);
            back.getIncoming().add(sequenceFlow);
            lastFlow = sequenceFlow;
        }

        return lastFlow;
    }

}
